`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2017/11/02 15:12:22
// Design Name: 
// Module Name: datapath
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module datapath (
    input wire clk,
    rst,
    //fetch stage
    output wire [31:0] pcF,
    input wire [31:0] instrF,
    //decode stage
    input wire branchD,
    input wire [1:0] jumpD,
    output wire [31:0] srca2D,
    srcb2D,
    output wire [5:0] opD,
    functD,
    output wire [4:0] rtD,
    //execute stage
    input wire [2:0] memtoregE,
    input wire alusrcE,
    regdstE,
    input wire regwriteE,
    input wire [5:0] alucontrolE,
    output wire flushE,
    input wire [1:0] hilo_enE,
    hiloreg_in_typeE,
    output wire stallE,
    input wire alu_srca_pcE,
    write_reg_31E,
    //mem stage
    input wire [2:0] memtoregM,
    input wire regwriteM,
    output wire [31:0] aluoutM,
    writedataM,
    input wire [31:0] readdataM,
    //writeback stage
    input wire [2:0] memtoregW,
    input wire regwriteW
);

  //fetch stage
  wire stallF;
  //FD
  wire [31:0] pcnextFD, pcnextbrFD, pcplus4F, pcbranchD;
  //decode stage
  wire [31:0] pcplus4D, instrD;
  wire forwardaD, forwardbD;
  wire [4:0] rsD, rdD, saD;
  wire flushD, stallD;
  wire [31:0] signimmD, signimmshD;
  wire [31:0] srcaD, srcbD;
  //   wire [31:0] srca2D, srcb2D;
  //execute stage
  wire [1:0] forwardaE, forwardbE;
  wire [4:0] rsE, rtE, rdE, saE;
  wire [ 4:0] writeregE;
  wire [31:0] signimmE;
  wire [31:0] srcaE, srca2E, srcbE, srcb2E, srcb3E;
  wire [31:0] aluoutE;

  wire [31:0] pcplus4E;
  wire [ 4:0] writeregE_final;
  //mem stage
  wire [ 4:0] writeregM;
  //writeback stage
  wire [ 4:0] writeregW;
  wire [31:0] aluoutW, readdataW, resultW;

  assign rtD = instrD[20:16];


  //next PC logic (operates in fetch an decode)
  mux2 #(32) pcbrmux (
      pcplus4F,
      pcbranchD,
      branchD,
      pcnextbrFD
  );
  mux3 #(32) pcmux (
      pcnextbrFD,
      {pcplus4D[31:28], instrD[25:0], 2'b00},
      srca2D,
      jumpD,
      pcnextFD
  );

  //regfile (operates in decode and writeback)
  regfile rf (
      clk,
      regwriteW,
      rsD,
      rtD,
      writeregW,
      resultW,
      srcaD,
      srcbD
  );

  //fetch stage logic
  pc #(32) pcreg (
      clk,
      rst,
      ~stallF,
      pcnextFD,
      pcF
  );
  adder pcadd1 (
      pcF,
      32'b100,
      pcplus4F
  );
  //decode stage
  flopenrc #(32) r1D (
      clk,
      rst,
      ~stallD,
      flushD,
      pcplus4F,
      pcplus4D
  );
  flopenrc #(32) r2D (
      clk,
      rst,
      ~stallD,
      flushD,
      instrF,
      instrD
  );
  signext se (
      instrD[15:0],
      signimmD
  );
  sl2 immsh (
      signimmD,
      signimmshD
  );
  adder pcadd2 (
      pcplus4D,
      signimmshD,
      pcbranchD
  );
  mux2 #(32) forwardamux (
      srcaD,
      aluoutM,
      forwardaD,
      srca2D
  );
  mux2 #(32) forwardbmux (
      srcbD,
      aluoutM,
      forwardbD,
      srcb2D
  );

  assign opD = instrD[31:26];
  assign functD = instrD[5:0];
  assign rsD = instrD[25:21];
  assign rtD = instrD[20:16];
  assign rdD = instrD[15:11];
  assign saD = instrD[10:6];

  //execute stage
  flopenrc #(32) r1E (
      clk,
      rst,
      ~stallE,
      flushE,
      srcaD,
      srcaE
  );
  flopenrc #(32) r2E (
      clk,
      rst,
      ~stallE,
      flushE,
      srcbD,
      srcbE
  );
  flopenrc #(32) r3E (
      clk,
      rst,
      ~stallE,
      flushE,
      signimmD,
      signimmE
  );
  flopenrc #(5) r4E (
      clk,
      rst,
      ~stallE,
      flushE,
      rsD,
      rsE
  );
  flopenrc #(5) r5E (
      clk,
      rst,
      ~stallE,
      flushE,
      rtD,
      rtE
  );
  flopenrc #(5) r6E (
      clk,
      rst,
      ~stallE,
      flushE,
      rdD,
      rdE
  );
  flopenrc #(5) r7E (
      clk,
      rst,
      ~stallE,
      flushE,
      saD,
      saE
  );
  flopenrc #(32) r8E (
      clk,
      rst,
      ~stallE,
      flushE,
      pcplus4D,
      pcplus4E
  );

  mux3 #(32) forwardaemux (
      srcaE,
      resultW,
      aluoutM,
      forwardaE,
      srca2E
  );
  mux3 #(32) forwardbemux (
      srcbE,
      resultW,
      aluoutM,
      forwardbE,
      srcb2E
  );
  mux2 #(32) srcbmux (
      srcb2E,
      signimmE,
      alusrcE,
      srcb3E
  );

  mux2 #(5) wrmux (
      rtE,
      rdE,
      regdstE,
      writeregE
  );

  wire overflow, zero;
  wire [31:0] alu_hi_out, alu_lo_out;
  wire [31:0] mul_hi_out, mul_lo_out;
  wire [31:0] hi_in, lo_in;  // 最终的hilo输入
  wire [31:0] hi_out, lo_out;  // 从hiloreg的输出
  wire [63:0] mux_temp_hilo;

  wire [31:0] alu_in_a;

  mux2 #(32) alu_srca_mux (
      srca2E,
      pcplus4E,
      alu_srca_pcE,
      alu_in_a
  );

  alu alu (
      alu_in_a,
      srcb3E,
      hi_out,
      lo_out,
      saE,
      alucontrolE,
      aluoutE,
      overflow,
      zero,
      start_div,
      signed_div,
      alu_hi_out,
      alu_lo_out
  );

  reg hiloreg_test_rst;
  initial begin
    hiloreg_test_rst = 1;
  end

  hiloreg hiloreg (
      .clk(clk),
      .rst(hiloreg_test_rst),
      .en(hilo_enE),
      .hi_in(hi_in),
      .lo_in(lo_in),
      .hi(hi_out),
      .lo(lo_out)
  );



  mux3 #(
      .WIDTH(64)
  ) hiloreg_in_mux (
      {alu_hi_out, alu_lo_out},
      {div_hi_out, div_lo_out},
      {mul_hi_out, mul_lo_out},
      hiloreg_in_typeE,
      mux_temp_hilo
  );

  assign {hi_in, lo_in} = mux_temp_hilo;

  wire [31:0] div_hi_out, div_lo_out;
  wire signed_div, start_div, div_running;
  reg annul_div;

  initial begin
    annul_div = 0;
  end
  div div (
      .clk(~clk),
      .rst(rst),

      .signed_div_i(signed_div),  // 是否是有符号除法
      .opdata1_i   (srca2E),      // 被除数
      .opdata2_i   (srcb3E),      // 除数
      .start_i     (start_div),   // 开始信号
      .annul_i     (annul_div),   // 终止信号

      .hi(div_hi_out),
      .lo(div_lo_out),
      .div_running(div_running)
  );



  mux2 #(5) writeregE_or_31 (
      writeregE,
      5'd31,
      write_reg_31E,
      writeregE_final
  );

  //mem stage
  flopr #(32) r1M (
      clk,
      rst,
      srcb2E,
      writedataM
  );
  flopr #(32) r2M (
      clk,
      rst,
      aluoutE,
      aluoutM
  );
  flopr #(5) r3M (
      clk,
      rst,
      writeregE_final,
      writeregM
  );

  //writeback stage
  flopr #(32) r1W (
      clk,
      rst,
      aluoutM,
      aluoutW
  );
  flopr #(32) r2W (
      clk,
      rst,
      readdataM,
      readdataW
  );
  flopr #(5) r3W (
      clk,
      rst,
      writeregM,
      writeregW
  );

  reg [31:0] readdata_extend;

  always @(readdataW, memtoregW) begin
    case (memtoregW)
      3'b001:  readdata_extend <= {{24{readdataW[7]}}, readdataW[7:0]};  //LB
      3'b101:  readdata_extend <= {{24{1'b0}}, readdataW[7:0]};  //LBU
      3'b010:  readdata_extend <= {{16{readdataW[15]}}, readdataW[15:0]};  //LH
      3'b110:  readdata_extend <= {{16{1'b0}}, readdataW[15:0]};  //LHU
      default: readdata_extend <= readdataW;
    endcase
  end

  wire use_mem;
  assign use_mem = memtoregW[0] | memtoregW[1] | memtoregW[2];

  mux2 #(32) resmux (
      aluoutW,
      readdata_extend,
      use_mem,
      resultW
  );

  wire hazard_memtoregE, hazard_memtoregM;

  assign hazard_memtoregE = (memtoregE[0] | memtoregE[1] | memtoregE[2]);
  assign hazard_memtoregM = (memtoregM[0] | memtoregM[1] | memtoregM[2]);

  //hazard detection
  hazard h (
      //fetch stage
      stallF,
      //decode stage
      rsD,
      rtD,
      branchD,
      jumpD,
      forwardaD,
      forwardbD,
      stallD,
      flushD,
      //execute stage
      rsE,
      rtE,
      writeregE,
      regwriteE,
      hazard_memtoregE,
      forwardaE,
      forwardbE,
      flushE,
      div_running,
      start_div,
      stallE,
      //mem stage
      writeregM,
      regwriteM,
      hazard_memtoregM,
      //write back stage
      writeregW,
      regwriteW
  );
endmodule
